// webpack: 是一个大包前端资源的工具，前端资源有很多不同的类型，js css 图片 字体，这些都需要http请求来获取

// 在开发web APP的时候，一般是一整个js加载到浏览器端之后，再把所有的内容给渲染出来，所以很多时候都可以简单的以JS文件作为入口
const path = require('path')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const webpack = require('webpack')
const HTMLPlugin = require('html-webpack-plugin')
const ExtractPlugin = require('extract-text-webpack-plugin')



const isDev = process.env.NODE_ENV === 'development'  // 在设置build和dev的环境的时候，NODE_ENV全部是保存在process.env这个对象里面的

const config = {
  target: "web", // 表示前端项目是在浏览器中跑的，所以webpack的编译目标是web平台
  // mode: "development",
  // 入口， 注意app.vue是一个组件，他不能直接挂在到HTML中， 在index.js中实现挂载
  entry: path.join(__dirname, "src/index.js"), // 这个方法实现的是路径拼接，拼成一个绝对路径，以保证能够访问到想要访问的那个文件， __dirname指的是项目的根路径
  // 出口
  output: {
    filename: "bundle.[hash:8].js", // 输出的文件名称
    path: path.join(__dirname, "dist")
  },
  // 以上输入和输出的含义是：
  // 输入一个js文件index.js，最终webpack会把这个文件以及这个文件中所依赖的（比如Vue，App）都打包成一个完整的bundle.js，
  // 并且，打包出来的是能够直接在浏览器中运行的js代码
  // 然后需要在package.json中添加一个build的脚本, 因为只有在这里面调用webpack他才会调用我们安装在这个项目里面的webpack，
  // 如果直接在命令行里面输入webpack的话，它会调用全局的webpack，全局的和我们项目中使用的webpack版本可能不同

  // 注意：webpack原生是只支持js文件类型的，并且支持的是ES5的语法， 所以需要一些处理工具，需要添加一个module
  module: {
    rules: [
      {
        test: /\.vue$/, // test用于检测文件类型
        loader: "vue-loader" // 加载器
      },
      // 需要加载其他文件内容就继续添加rules
      {
        test: /\.jsx$/, 
        loader: "babel-loader" // 使用babel加载  .jsx 的文件
      },
      // {
      //   test: /\.css$/, // css，在前端项目运行的时候，要作为一个外部文件去处理的，
      //   // 或者是把样式写到HTML内容当中，作为一个style标签，然后把里面的样式全部列出来，所以有不同的处理方式，使用use
      //   use: [
      //     // css-loader只是帮我们把css'文件中的内容，最终是需要把它写到一个新的文件里面还是插到HTML里面，需要用别的loader帮助我们来处理
      //     "vue-style-loader",
      //     "css-loader"
      //     // 增加了style-loader，这样的话，写好的css会在js中以一段js代码出现，这段js代码的作用是帮助我们把css写到html当中
      //     // 注意以上两个loader的书写顺序！！！
      //     // css-loader会遍历所有的css文件，找到所有的url（...）并进行处理，
      //     // style-loader则是把所有的样式插入到页面的style标签里面，
      //     // 因此，这个顺序是先加载css-loader再加载style-loader，
      //     // 但是在webpack.config.js文件里面，loader的解析是逆序的，因此要把style-loader写在css-loader的前面
      //   ]
      // },
      // 使用一个css预处理器： 可以使用模块化的方式去写css代码 ，而不是以.css的方式写原声代码， 这种方式可以写的内容比较少
      
      // 图片,图片的格式比较多，书写格式需要注意
      {
        test: /\.(png|gif|jpg|jpeg|svg)$/,
        use: [
          {
            // 写成一个对象的形式，因为每个loader都会有一些选项需要配置
            loader: "url-loader", // url-loader可以帮我们把图片转换成base64代码，直接写在js内容里面而不用生成一个新的文件， 对于一些小的图片作用很大，可以减少http请求
            options: {
              // 在这里面就可以把一些参数传给url-loader，来指定它的操作方式
              limit: 1024, // url-loader其实是file-loader的一个封装， file-loader会将图片读取，然后进行一些简单的操作之后再把这个图片文件重新换一个名字换个地方存储，
              // url-loader在file-loader的基础上多做了一件事，它可以判断一下如果图片的大小小于1024，它就会将图片转译成base64的代码写到代码当中
              name: "[name].[ext]" // 因为有很多的图片，如果把名字写死，每个都是相同的了，图片的名字可以使用webpack的选项来定义， 根据它进来时候的name设置名字
            }
          }
        ]
      }
      // 注意： 以上的loader配置完成之后都需要npm安装一下.
    ]
  },
  plugins: [
    // vue-loader was used without the corresponding plugin. Make sure to include VueLoaderPlugin in your webpack config.
    new VueLoaderPlugin(),
    
    // 在使用vue或者react的时候一定需要使用到webpack的DefinePlugin，
    // 他是给webpack在编译的过程中，以及自己写的JS代码的环境都可以使用调用process.env.NODE_ENV进行判断，也就是说在这里定义完成之后，在JS代码中是可以引用的
    // vue会根据不同的环境区分打包，vue的dist文件中会有很多的不同版本的vue的源代码，在开发环境下是一个比较大的版本，里面会包含很多的错误信息的提示信息以及很多的功能
    // 这些功能在正式环境下是不希望被使用的，因为他会加大文件的大小，会降低整个代码的运行效率，
    // 在开发环境下使用development是有很多的好处的，可以得到在渲染时候的一些错误提醒。

    new webpack.DefinePlugin({
      "process.env": {
        NODE_ENV: isDev ? '"development"' : '"production"'
      }
    }),
    new HTMLPlugin()
  ]
};

// 因为这个文件是会同时使用的正式环境和开发环境下的，所以需要 根据不同的环境做一些判断
// 如何判断？ 设置一个环境变量，来表示显示使用的是开发环境还是正式环境，
// 具体的：  安装一个包 npm i cross-env,  
// 因为在不同的平台上设置环境变量的方式不同，mac上面可以直接设置NODE_ENV=production就能读取这个环境变量，但是在windows上面可能需要添加set等，
// 使用cross-env就免除了不同平台不同命令的写法， 这样的话就可以在不同的平台上执行不同的脚本，具体的设置在package.json中的build和dev中。

// "build": "webpack --config webpack.config.js",   // 这句话表示，在build的时候，使用webpack去启动webpack.config.js，完成资源打包
// "dev": "webpack-dev-server --config webpack.config.js"  // 使用的是同一个webpack的配置文件，而webpack-dev-server与webpack带来的效果不同，他是专门使用在开发环境的， 然后需要去修改一下webpack的一些配置，来适应webpack-dev-server的一个开发模式

// 设置环境变量之后就是这样的：
// "build": "cross-env NODE_ENV=production webpack --config webpack.config.js",
// "dev": "cross-env NODE_ENV=development webpack-dev-server --config webpack.config.js"

// 接下来就是设置环境变量

if (isDev) {
    config.module.rules.push({
      test: /\.styl/,
      use: [
        'vue-style-loader',
        'css-loader',
        {
          loader: 'postcss-loader',
          options: {
            sourceMap: true // 这个配置表示
          }
        },
        'stylus-loader' // 专门用来处理stylus文件的，处理完成之后是css内容， 然后css要怎么处理他还要给到css-loader，  webpack的loader是一层一层往上扔的
        // 这就表示，每一个loader只会处理它关心的那一部分， 所以stylus-loader只处理stylus代码，生成标准的css代码，至于接下来css代码需要进行什么处理他是不关心的
        // 所以，可以使用很多种的loader来进行搭配，来实现自己想要的一个结果

      ]
    })

    config.devtool = '#cheap-module-eval-source-map'  // 这个配置是帮助我们在页面上调试代码的，因为我们使用的是.vue文件的开发模式的，写的都是ES6的代码
    // 这些代码在浏览器中是无法直接运行的，所以如果直接调试浏览器中的代码，代码都是经过编译的，可能都读不懂，使用的方式就是使用source map进行代码的映射，
    // 这样的话，打开的就是我们自己写的代码的样子,  可以很快的进行错误定位。 
    // 这个配置比较长，因为可能会有很多不同的source map 的映射方式，  source map可以更完整的映射代码和编译之后的代码之间的关系，但是它的效率比较低，文件也是很大的，
    // 所以会导致webpack的编译也会比较慢，在页面上进行调试的话也是会比较慢，如果使用eval的话，就会让代码看起来比较乱，代码对应补齐，找错误不好定位
    // 官方提供的这个配置， 效率比较高，准确性也是比较高的

    // devServer是在webpack2以后才加入的
    config.devServer = {
      // 端口
      port: 8000,
      host: '0.0.0.0',  // 这样设置的原因是， 我们可以通过localhost127.0.0.1进行访问，同时也可以使用本机的内网IP进行访问，如果直接设置成localhost，那么通过内网的ip是访问不了的
      overlay: {  // 表示webpack在编译的过程中如果出现任何的错误，都将这个错误显示到网页上面
          errors: true
      },

      // open: true  // 表示在  webpack-dev-server的时候会自动打开浏览器，

      // historyFallback: {
        // 因为我们做的是单页应用，单页应用会做很多的前端路由，页面请求进来的地址不一定就是默认的这个index.html，
        // historyFallback能够帮我们把webpack-dev-server本身不理解的这些地址，没有做映射的这些地址都映射到入口index.html上面
      // }

      hot: true   // 热加载：HotModuleReplacement，  
      // 在开发单页应用的时候，页面上会有很多的数据，如果每次一改代码，整个页面就刷新了
      // 就会导致页面上的数据需要重新刷新一遍才会回来，所以hot的功能就是，在修改了一个组件的代码的时候，
      // 只会重新渲染当前这个组件的一个效果就可以了，而不会让整个页面都重新加载
    }

    config.plugins.push(
      new webpack.HotModuleReplacementPlugin(),  // 启动热加载功能的插件
      new webpack.NoEmitOnErrorsPlugin()   // 这个插件不是必须的，但是他会帮我们减少一些不需要的信息的展示
    )
}else {
  // 正式环境
  config.entry = {
    app: path.join(__dirname, "src/index.js"),
    vendor: ['vue']
  }
  config.output.filename = '[name].[chunkhash:8].js'
  config.module.rules.push(
    { 
      test: /\.styl/,
      use: ExtractPlugin.extract({
        fallback: 'style-loader',
        use: [
          // 'vue-style-loader',  在这里，这一部分就需要去掉，
          // 因为我们是要把样式单独的写到一个css文件中，所以css-loader处理出来的内容已经够了，
          // fallback中的style-loader其实是在css-loader处理的内容的外面包了一层JS代码，
          // 这部分JS代码就是把css代码写成一个style标签并写到HTML里面去， 所以这段代码在css文件中是没有用的，所以需要这样去配置
          'css-loader', {
            loader: 'postcss-loader',
            options: {
              sourceMap: true 
            }
          },
          'stylus-loader'
        ]
      })
    }
  )

  config.plugins.push(
    new ExtractPlugin('styles.[md5:contentHash:hex:8].css'),
    // 参数指的是输出的静态文件的名字，contentHash会根据我们输出的这个css文件的内容进行一个哈希然后得到一个单独的值
  )

  config.optimization = {
    splitChunks: {
      cacheGroups: {
        commons: {
          name: "vendor",  //这里的vendor和entry里定义的名字一致，可自行更换
          chunks: "initial",
          minChunks: 2
        }
      }
    }
  };
}

// 配置完环境变量后，我们的前端项目一定得需要一个HTML文件作为入口， 那这个HTML要怎么做才能让它自动包含JS？
// 使用webpack的一个组件： npm i html-webpack-plugin
module.exports = config
